/*
 * @Author: your name
 * @Date: 2020-11-13 11:34:00
 * @LastEditTime: 2021-01-13 13:49:45
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \myReposCode\Thread\FactoryConsumer.cpp
 */
#include <iostream>
#include <queue>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <random>
#include <chrono>

using std::cout;
using std::default_random_engine;
using std::endl;
using std::mutex;
using std::queue;
using std::thread;
using std::unique_lock;

class FactoryConsumer
{
public:
    FactoryConsumer() : m_count(0), m_num(0){};
    void factory()
    {
        while (true) // 通过外层循环，能保证生产不停止
        {
            unique_lock<mutex> my_lock_f(m_mux); // ------------注意此处位置while内部
            if (m_count < 8) // 限流
            {
                m_num = m_rand();
                m_que_data.push(m_num);
                cout << "push --- num: " << m_num << endl;
                ++m_count;
                my_lock_f.unlock();
                m_cond.notify_one(); // 唤醒 wait()
                std::this_thread::sleep_for(std::chrono::milliseconds(20));
            }
        }
    }

    void consumer()
    {
        int temp = 0; // 用来存放取出的数据
        {
            unique_lock<mutex> mu_lock_c(m_mux);// ------------注意此处位置while外部即可，因为lock和unlock都交给wait了
            while (true)
            {
                // if (m_que_data.empty())
                // {
                //     m_cond.wait(mu_lock_c);
                // }

                // 下方代码等价于上方注释代码
                //wait()阻塞前先会解锁,解锁后生产者才能获得锁来放产品到缓冲区；生产者notify后，将不再阻塞，且自动又获得了锁。
                m_cond.wait(mu_lock_c, [this]() { 
                    return !m_que_data.empty();
                });

                temp = m_que_data.front(); // 取数据第一步
                m_que_data.pop();          // 取数据第二步
                cout << "pop num: " << temp << endl;
                --m_count;
                // cout << "count num: " << m_count << endl;
            }
        }
    }

private:
    std::queue<int> m_que_data;   // 产品queue容器
    std::condition_variable m_cond;
    std::mutex m_mux;
    default_random_engine m_rand; // 随机数
    int m_count;                  // 缓冲区的产品个数
    int m_num;                    // 产品
};

int main(int argc, char **argv)
{
    FactoryConsumer my_obj;
    thread th_2(&FactoryConsumer::consumer, &my_obj);
    thread th_1(&FactoryConsumer::factory, &my_obj);

    th_2.join();
    th_1.join();


    return EXIT_SUCCESS;
}